package com.atguigu.yygh.order.service.impl;

import com.atguigu.yygh.enums.RefundStatusEnum;
import com.atguigu.yygh.hosp.globalexp.YyghException;
import com.atguigu.yygh.model.order.OrderInfo;
import com.atguigu.yygh.model.order.PaymentInfo;
import com.atguigu.yygh.model.order.RefundInfo;
import com.atguigu.yygh.order.service.OrderInfoService;
import com.atguigu.yygh.order.service.PaymentInfoService;
import com.atguigu.yygh.order.service.RefundService;
import com.atguigu.yygh.order.service.WeixinService;
import com.atguigu.yygh.order.util.ConstantWeixinPayUtils;
import com.atguigu.yygh.order.util.HttpClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.wxpay.sdk.WXPayUtil;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class WeixinServiceImpl implements WeixinService {

    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    PaymentInfoService paymentInfoService;
    @Autowired
    OrderInfoService orderInfoService;

    @Autowired
    RefundService refundService;
    @Override
    public String createNative(Long orderId) {
        //1.根据订单id,去redis中查询该订单对应的支付链接是否存在,如果存在直接返回
        String codeUrl = stringRedisTemplate.boundValueOps(String.valueOf(orderId)).get();
        if(!StringUtils.isEmpty(codeUrl)){
            return codeUrl;
        }
        //2.判断该订单对应的支付记录是否存在,如果不存在就创建一个支付记录
        paymentInfoService.createPaymentInfo(orderId);
        //3.调用微信支付端"统一下单"接口获取codeUrl
        String url="https://api.mch.weixin.qq.com/pay/unifiedorder";

        //将该url接口所需要的参数封装成一个map对象
        Map<String,String> data=new HashMap<>();
        data.put("appid",ConstantWeixinPayUtils.APPID);
        data.put("mch_id",ConstantWeixinPayUtils.PARTNER);
        data.put("nonce_str",WXPayUtil.generateNonceStr());

       // data.put("sign",xxx); WXPayUtil.generateSignedXml()方法会使用第二个参数自动生成sign签名

        //2023-08-18 就诊于xxx科室
        OrderInfo orderInfo = orderInfoService.getById(orderId);
        String body = new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd")+"就诊于"+orderInfo.getDepname();
        data.put("body",body);
        data.put("out_trade_no",orderInfo.getOutTradeNo());  //订单的编号

        BigDecimal amount = orderInfo.getAmount();  //元
        BigDecimal multiply = amount.multiply(new BigDecimal(100));
        long fee = multiply.longValue();
        //data.put("total_fee",String.valueOf(fee));  //单位为分(扫码后要支付的金额)
        data.put("total_fee","1"); //1分钱
        data.put("spbill_create_ip","127.0.0.1");

        //用于异步接收支付状态的接口(必须是一个外网可访问的域名)
        //并没有实际使用这个参数,至于支付状态,我们会自己调用微信端的"查询订单"接口
        data.put("notify_url","http://guli.shop/api/order/weixinPay/weixinNotify");
        data.put("trade_type","NATIVE"); //交易类型,商户生成二维码


        //参数1:将参数封装成map
        //参数2:表示使用商户秘钥生成一个签名
        //返回字符串,xml格式的字符串
        try {
            String xml = WXPayUtil.generateSignedXml(data, ConstantWeixinPayUtils.PARTNERKEY);

            //使用HttpClient工具类调用微信端接口,调用的代码写法都是一样的
            HttpClient httpClient = new HttpClient(url);  //url
            httpClient.setXmlParam(xml); //参数
            httpClient.setHttps(true); //协议
            httpClient.post(); //请求类型


            //获取微信端接口返回的数据(xml格式的字符串)
            String content = httpClient.getContent();

            //xml格式的字符串转成map对象
            Map<String,String> map=WXPayUtil.xmlToMap(content);
            codeUrl=map.get("code_url");

            if(StringUtils.isEmpty(codeUrl)){
                throw new YyghException(20001,"获取支付链接失败");
            }

            ////4.存储到redis 5分钟
            stringRedisTemplate.boundValueOps(String.valueOf(orderId)).set(codeUrl,5, TimeUnit.MINUTES);
            return codeUrl;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Map<String, String> queryPayStatus(Long orderId) {

        OrderInfo orderInfo = orderInfoService.getById(orderId);
        String url="https://api.mch.weixin.qq.com/pay/orderquery";

        Map<String,String> data=new HashMap<>();
        data.put("appid",ConstantWeixinPayUtils.APPID);
        data.put("mch_id",ConstantWeixinPayUtils.PARTNER);
        data.put("out_trade_no",orderInfo.getOutTradeNo());
        data.put("nonce_str",WXPayUtil.generateNonceStr());

        try {
           String xml = WXPayUtil.generateSignedXml(data,ConstantWeixinPayUtils.PARTNERKEY);

            HttpClient httpClient = new HttpClient(url);
            httpClient.setXmlParam(xml);
            httpClient.setHttps(true);
            httpClient.post();

            //微信端返回值
            String content = httpClient.getContent();
            Map<String, String> map = WXPayUtil.xmlToMap(content);
            return map;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //退款流程
    @Override
    public void refund(Long orderId) {
        //1.根据orderId查询该订单对应的退款记录,订单的退款记录最多一个,如果不存在创建一个默认的退款记录
        QueryWrapper<RefundInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("order_id",orderId);
        RefundInfo refundInfo = refundService.getOne(wrapper);

        //查询支付记录,因为退款记录中的很多字段和支付记录中的数据是一致的
        PaymentInfo paymentInfo=paymentInfoService.getByOrderId(orderId);
        if(refundInfo==null){
            refundInfo=new RefundInfo();

            refundInfo.setOutTradeNo(paymentInfo.getOutTradeNo());
            refundInfo.setOrderId(orderId);
            refundInfo.setPaymentType(paymentInfo.getPaymentType());
            refundInfo.setTotalAmount(paymentInfo.getTotalAmount());
            refundInfo.setSubject(paymentInfo.getSubject());
            refundInfo.setRefundStatus(RefundStatusEnum.UNREFUND.getStatus());
            refundService.save(refundInfo);
        }

        //2.判断该订单的退款记录的状态,如果=2表示已退款,不需要调用微信端的退款接口,否则调用微信端的退款接口
        if(refundInfo.getRefundStatus()==RefundStatusEnum.UNREFUND.getStatus()){
            //退款记录的状态为未退款,调用微信端的退款接口
            String url= "https://api.mch.weixin.qq.com/secapi/pay/refund";
            Map<String,String> paramMap = new HashMap<>();
            paramMap.put("appid",ConstantWeixinPayUtils.APPID);
            paramMap.put("mch_id",ConstantWeixinPayUtils.PARTNER);
            paramMap.put("nonce_str",WXPayUtil.generateNonceStr());
            paramMap.put("out_trade_no",refundInfo.getOutTradeNo());//退款时使用的outTradeNo必须和创建支付链接时使用的必须一致
            paramMap.put("out_refund_no","tk_"+refundInfo.getOutTradeNo());//退款单号，保证唯一即可
            paramMap.put("total_fee","1");
            paramMap.put("refund_fee","1");

            try {
                String xml = WXPayUtil.generateSignedXml(paramMap, ConstantWeixinPayUtils.PARTNERKEY);
                HttpClient httpClient=new HttpClient(url);
                httpClient.setXmlParam(xml);
                httpClient.setHttps(true);
                httpClient.setCert(true); //需要证书
                httpClient.setCertPassword(ConstantWeixinPayUtils.PARTNER);  //访问证书的凭证(密码)
                httpClient.post();


                //微信端返回值
                String content = httpClient.getContent();
                Map<String, String> map = WXPayUtil.xmlToMap(content);

                //返回值如何判断是否退款成功?
                String result_code = map.get("result_code");
                if("SUCCESS".equalsIgnoreCase(result_code)){
                    //讲义上认为这样就算是退款成功了(严格讲只能表示退款接口调用成功了,具体是否退款成功,需要调用)
                    //查询退款接口--refund_status_$n 退款状态,SUCCESS--退款成功

                    //3.如果退款成功,为退款记录的trade_no+callback_content+callback_time+refund_status
                    refundInfo.setRefundStatus(RefundStatusEnum.REFUND.getStatus());//退款记录的状态改成已退款
                    refundInfo.setTradeNo(map.get("refund_id")); //退款流水号(微信记录的状态改成已退款)
                    refundInfo.setCallbackTime(new Date());
                    refundInfo.setCallbackContent(map.toString());
                    refundService.updateById(refundInfo);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


    }
}
